home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockMetricsService.js < prev    next >
Text File  |  2007-10-12  |  18KB  |  645 lines

  1. //
  2. // BEGIN FLOCK GPL
  3. // 
  4. // Copyright Flock Inc. 2005-2007
  5. // http://flock.com
  6. // 
  7. // This file may be used under the terms of of the
  8. // GNU General Public License Version 2 or later (the "GPL"),
  9. // http://www.gnu.org/licenses/gpl.html
  10. // 
  11. // Software distributed under the License is distributed on an "AS IS" basis,
  12. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. // for the specific language governing rights and limitations under the
  14. // License.
  15. // 
  16. // END FLOCK GPL
  17. //
  18.  
  19. const MS_CONTRACTID = '@flock.com/metrics-service;1';
  20. const MS_CLASSID    = Components.ID('{bc1358fa-6e36-4d0d-a401-b0b02111114c}');
  21. const MS_CLASSNAME  = 'Flock Metrics Service';
  22.  
  23.  
  24. const ENABLED_BY_DEFAULT          = true;
  25.  
  26. const LOGGING_URL                 = 'http://metrics.flock.com/collect.php';
  27.  
  28. const PREF_FLOCK_METRICS_ENABLED  = 'flock.metrics.enabled';
  29. const PREF_FLOCK_METRICS_SHUTDOWN = 'flock.metrics.shutdownClean';
  30.  
  31. const PREF_FLOCK_METRICS_INTERVAL = 'flock.metrics.interval';
  32. const DEFAULT_METRICS_INTERVAL    = 86400;
  33.  
  34. const PREF_FLOCK_FIRST_RUN_DATE   = 'flock.first_run.bigDate';
  35. const PREF_USERAGENT_EDITION      = 'general.useragent.edition';
  36.  
  37. const PREF_DEFAULT_SEARCH_ENGINE  = 'browser.search.selectedEngine';
  38. const PREF_DEFAULT_ENGINE_NAME    = 'browser.search.defaultenginename';
  39. const PREF_MYWORLD_SEARCH_ENGINE  = 'flock.myworld.currentEngine';
  40. const PREF_START_PAGE             = 'browser.startup.homepage';
  41. const PREF_FEED_SELECTED_ACTION   = 'browser.feeds.handler';
  42.  
  43. const URI_BRAND_PROPERTIES        = 'chrome://branding/locale/brand.properties';
  44.  
  45. const MYWORLD_URL                 = 'about:myworld';
  46.  
  47.  
  48. const Cc = Components.classes;
  49. const Ci = Components.interfaces;
  50. const Cr = Components.results;
  51.  
  52. /* from nspr's prio.h */
  53. const PR_RDONLY      = 0x01;
  54. const PR_WRONLY      = 0x02;
  55. const PR_RDWR        = 0x04;
  56. const PR_CREATE_FILE = 0x08;
  57. const PR_APPEND      = 0x10;
  58. const PR_TRUNCATE    = 0x20;
  59. const PR_SYNC        = 0x40;
  60. const PR_EXCL        = 0x80;
  61.  
  62.  
  63. var gApp = null;
  64.  
  65.  
  66. function getObserverService() {
  67.   return Cc['@mozilla.org/observer-service;1']
  68.     .getService(Ci.nsIObserverService);
  69. }
  70.  
  71. function getCharPref(prefName, defaultValue) {
  72.   try {
  73.     var prefs = Cc['@mozilla.org/preferences-service;1']
  74.       .getService(Ci.nsIPrefBranch);
  75.     return prefs.getCharPref(prefName);
  76.   }
  77.   catch (e) {
  78.     return defaultValue;
  79.   }
  80. }
  81.  
  82. function getIntPref(prefName, defaultValue) {
  83.   try {
  84.     var prefs = Cc['@mozilla.org/preferences-service;1']
  85.       .getService(Ci.nsIPrefBranch);
  86.     return prefs.getIntPref(prefName);
  87.   }
  88.   catch (e) {
  89.     return defaultValue;
  90.   }
  91. }
  92.  
  93. function getBoolPref(prefName, defaultValue) {
  94.   try {
  95.     var prefs = Cc['@mozilla.org/preferences-service;1']
  96.       .getService(Ci.nsIPrefBranch);
  97.     return prefs.getBoolPref(prefName);
  98.   }
  99.   catch (e) {
  100.     return defaultValue;
  101.   }
  102. }
  103.  
  104.  
  105. function MetricsService() {
  106.   gApp = Cc['@mozilla.org/xre/app-info;1']
  107.     .getService(Ci.nsIXULAppInfo)
  108.     .QueryInterface(Ci.nsIXULRuntime);
  109.  
  110.   this._createStore();
  111.  
  112.   var obs = getObserverService();
  113.  
  114.   obs.addObserver(this, 'flock-data-ready', false);
  115.   obs.addObserver(this, 'quit-application', false);
  116.   obs.addObserver(this, 'xpcom-shutdown', false);
  117.  
  118.   this._enabled = ENABLED_BY_DEFAULT;
  119.  
  120.   if (this._enabled)
  121.     this.observe(null, 'nsPref:changed', null);
  122. }
  123.  
  124. MetricsService.prototype = {
  125.   _start: function MS__start() {
  126.     this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  127.     this._logger.init('metrics');
  128.     this._logger.info('starting up...');
  129.  
  130.     this._saveTimer = null;
  131.  
  132.     this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton)
  133.       .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  134.       .wrappedJSObject;
  135.  
  136.     var prefs = Cc['@mozilla.org/preferences-service;1']
  137.       .getService(Ci.nsIPrefBranch2);
  138.     prefs.addObserver(PREF_FLOCK_METRICS_ENABLED, this, false);
  139.  
  140.     this.observe(null, 'nsPref:changed', null);
  141.  
  142.     this._watchAuthEvents();
  143.  
  144.     if (prefs.prefHasUserValue(PREF_FLOCK_METRICS_SHUTDOWN) &&
  145.         prefs.getBoolPref(PREF_FLOCK_METRICS_SHUTDOWN) == false)
  146.       this.reportCount('crash');
  147.  
  148.     prefs.setBoolPref(PREF_FLOCK_METRICS_SHUTDOWN, false);
  149.  
  150.     prefs.QueryInterface(Ci.nsIPrefService);
  151.     prefs.savePrefFile(null);
  152.  
  153.     this.reportCount('start');
  154.  
  155.     var tm = Cc['@mozilla.org/updates/timer-manager;1']
  156.       .getService(Ci.nsIUpdateTimerManager);
  157.     var interval = getIntPref(PREF_FLOCK_METRICS_INTERVAL,
  158.                               DEFAULT_METRICS_INTERVAL);
  159.     tm.registerTimer('background-metrics-timer', this, interval);
  160.   },
  161.   _stop: function MS__stop() {
  162.     if (this._saveTimer) {
  163.       this._saveTimer.cancel();
  164.       this._saveTimer = null;
  165.     }
  166.  
  167.     this._saveStore();
  168.  
  169.     var prefs = Cc['@mozilla.org/preferences-service;1']
  170.       .getService(Ci.nsIPrefBranch);
  171.     prefs.setBoolPref(PREF_FLOCK_METRICS_SHUTDOWN, true);
  172.   },
  173.   _shutdown: function MS__shutdown() {
  174.     gApp = null;
  175.   },
  176.  
  177.   _configure: function MS__configure() {
  178.     this._enabled = getBoolPref(PREF_FLOCK_METRICS_ENABLED, ENABLED_BY_DEFAULT);
  179.  
  180.     if (!this._enabled) {
  181.       var file = this._getMetricsFile();
  182.       try {
  183.         file.remove(false);
  184.       }
  185.       catch (e) { }
  186.     }
  187.   },
  188.   _prefChanged: function MS__prefChanged(state) {
  189.     this._configure();
  190.   },
  191.  
  192.   observe: function MS_observe(subject, topic, state) {
  193.     var obs = getObserverService();
  194.  
  195.     switch (topic) {
  196.       case 'flock-data-ready':
  197.         obs.removeObserver(this, 'flock-data-ready');
  198.         this._start();
  199.         break;
  200.  
  201.       case 'quit-application':
  202.         obs.removeObserver(this, 'quit-application');
  203.         this._stop();
  204.         break;
  205.  
  206.       case 'xpcom-shutdown':
  207.         obs.removeObserver(this, 'xpcom-shutdown');
  208.         this._shutdown();
  209.         break;
  210.  
  211.       case 'nsPref:changed':
  212.         this._prefChanged(state);
  213.         break;
  214.  
  215.       case 'timer-callback':
  216.         this._saveTimer = null;
  217.         this._saveStore();
  218.         break;
  219.     }
  220.   },
  221.  
  222.   notify: function MS_notify(timer) {
  223.     this._sendReport();
  224.   },
  225.  
  226.   _getMetricsFile: function MS__getMetricsFile() {
  227.     var file = Cc['@mozilla.org/file/directory_service;1']
  228.       .getService(Ci.nsIProperties).get('ProfD', Ci.nsILocalFile);
  229.     file.append('mstore.js');
  230.     return file;
  231.   },
  232.  
  233.   _fillBaseInfo: function MS__fillBaseInfo(store) {
  234.     store['version'] = gApp.version;
  235.     store['buildID'] = gApp.appBuildID;
  236.     store['firstRun'] = getCharPref(PREF_FLOCK_FIRST_RUN_DATE, '0');
  237.     store['edition'] = getCharPref(PREF_USERAGENT_EDITION, '');
  238.  
  239.     var sbs = Cc['@mozilla.org/intl/stringbundle;1']
  240.       .getService(Ci.nsIStringBundleService);
  241.     var bundle = sbs.createBundle(URI_BRAND_PROPERTIES);
  242.  
  243.     store['product'] = bundle.GetStringFromName('brandShortName');
  244.   },
  245.   _fillPrefInfo: function MS__fillPrefInfo(store) {
  246.     var prefs = Cc['@mozilla.org/preferences-service;1']
  247.       .getService(Ci.nsIPrefBranch);
  248.  
  249.     var defaultEngine;
  250.     try {
  251.       defaultEngine = prefs.getCharPref(PREF_DEFAULT_SEARCH_ENGINE);
  252.     }
  253.     catch (e) {
  254.       defaultEngine = prefs.getComplexValue(PREF_DEFAULT_ENGINE_NAME,
  255.                                             Ci.nsIPrefLocalizedString).data;
  256.     }
  257.  
  258.     if (!defaultEngine)
  259.       defaultEngine = '';
  260.  
  261.     store['default search engine'] = defaultEngine;
  262.  
  263.     var startPageHasMyWorld = false;
  264.     try {
  265.       var startPage = prefs.getComplexValue(PREF_START_PAGE,
  266.                                             Ci.nsIPrefLocalizedString).data;
  267.       var startPages = startPage.split('|');
  268.       for each (var url in startPages) {
  269.         // Trim leading/trailing whitespace from URL
  270.         url = url.replace(/(^\s+)|(\s+$)/g, "");
  271.         if (url == MYWORLD_URL) {
  272.           startPageHasMyWorld = true;
  273.           break;
  274.         }
  275.       }
  276.     }
  277.     catch (e) { }
  278.  
  279.     store['myworld start page'] = startPageHasMyWorld;
  280.  
  281.     store['myworld search engine'] =
  282.       getCharPref(PREF_MYWORLD_SEARCH_ENGINE, '');
  283.  
  284.     var action = getCharPref(PREF_FEED_SELECTED_ACTION);
  285.     var feedReader;
  286.     if (action == 'ask')
  287.       feedReader = 'news';
  288.     else if (action == 'bookmarks')
  289.       feedReader = 'livemarks';
  290.     else
  291.       feedReader = 'other';
  292.  
  293.     store['default feed reader'] = feedReader;
  294.  
  295.     store['metrics enabled'] = this._enabled;
  296.   },
  297.  
  298.   _initStore: function MS__initStore() {
  299.     this._store = {};
  300.     this._fillBaseInfo(this._store);
  301.     this._fillPrefInfo(this._store);
  302.   },
  303.   _createStore: function MS__createStore() {
  304.     var store;
  305.  
  306.     try {
  307.       var file = this._getMetricsFile();
  308.  
  309.       var stream = Cc['@mozilla.org/network/file-input-stream;1']
  310.         .createInstance(Ci.nsIFileInputStream);
  311.       stream.init(file, PR_RDONLY, 0, 0);
  312.  
  313.       var cvstream = Cc['@mozilla.org/intl/converter-input-stream;1']
  314.         .createInstance(Ci.nsIConverterInputStream);
  315.       cvstream.init(stream, 'UTF-8', 1024,
  316.                     Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  317.  
  318.       var content = '';
  319.       var data = {};
  320.       while (cvstream.readString(4096, data)) {
  321.         content += data.value;
  322.       }
  323.       cvstream.close();
  324.  
  325.       store = content.replace(/\r\n?/g, '\n');
  326.     }
  327.     catch (e) {
  328.       store = null;
  329.     }
  330.  
  331.     if (store) {
  332.       try {
  333.         this._store = this._safeEval(store);
  334.         return;
  335.       }
  336.       catch (e) { }
  337.     }
  338.  
  339.     this._initStore();
  340.   },
  341.  
  342.   _saveStore: function MS__saveStore() {
  343.     if (!this._enabled) return;
  344.  
  345.     try {
  346.       var file = this._getMetricsFile();
  347.  
  348.       var ostream = Cc['@mozilla.org/network/safe-file-output-stream;1']
  349.         .createInstance(Ci.nsIFileOutputStream);
  350.       ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, 0);
  351.  
  352.       var converter = Cc['@mozilla.org/intl/scriptableunicodeconverter']
  353.         .createInstance(Ci.nsIScriptableUnicodeConverter);
  354.       converter.charset = 'UTF-8';
  355.  
  356.       var data = this._store.toSource();
  357.       var convdata = converter.ConvertFromUnicode(data) + converter.Finish();
  358.  
  359.       ostream.write(convdata, convdata.length);
  360.  
  361.       if (ostream instanceof Ci.nsISafeOutputStream) {
  362.         ostream.finish();
  363.       } else {
  364.         ostream.close();
  365.       }
  366.     }
  367.     catch (e) { }
  368.   },
  369.  
  370.   _scheduleSaveStore: function MS__scheduleSaveStore() {
  371.     if (!this._enabled) return;
  372.  
  373.     if (!this._saveTimer) {
  374.       this._saveTimer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  375.       this._saveTimer.init(this, 5000, Ci.nsITimer.TYPE_ONE_SHOT);
  376.     }
  377.   },
  378.  
  379.   _watchAuthEvents: function MS__watchAuthEvents() {
  380.     var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
  381.       .getService(Ci.nsIRDFService);
  382.     var isAuth = RDFS.GetResource("http://flock.com/rdf#isAuthenticated");
  383.  
  384.     var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
  385.       .getService(Ci.flockIRDFObservable);
  386.     faves.addArcObserver(Ci.flockIRDFObserver.WATCH_TYPES, null,
  387.                          isAuth, RDFS.GetLiteral("true"), this);
  388.   },
  389.   rdfChanged: function MS_rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
  390.     var acct = this._coop.get_from_resource(rsrc);
  391.     var svc = Cc[acct.serviceId].getService(Ci.flockIWebService);
  392.     this.reportCount('login ' + svc.shortName);
  393.   },
  394.  
  395.   report: function MS_report(key, value) {
  396.     if (!this._enabled) return;
  397.     this._store[key] = value;
  398.     this._scheduleSaveStore();
  399.   },
  400.   reportNow: function MS_reportNow(key, value) {
  401.     if (this._enabled)
  402.       this._sendQuickReport(key, value);
  403.   },
  404.   reportCount: function MS_reportCount(key) {
  405.     if (!this._enabled) return;
  406.  
  407.     if (this._store[key])
  408.       this._store[key]++;
  409.     else
  410.       this._store[key] = 1;
  411.  
  412.     this._scheduleSaveStore();
  413.   },
  414.  
  415.   _sendReport: function MS__sendReport() {
  416.     if (this._enabled) {
  417.       this._fillBaseInfo(this._store);
  418.       this._fillPrefInfo(this._store);
  419.       this._sendData(this._toJSONString(this._store));
  420.     } else {
  421.       this._sendQuickReport('metrics enabled', false);
  422.     }
  423.  
  424.     this._initStore();
  425.     this._saveStore();
  426.   },
  427.   _sendQuickReport: function MS__sendQuickReport(key, value) {
  428.     var data = {};
  429.     data[key] = value;
  430.     this._fillBaseInfo(data);
  431.  
  432.     this._sendData(this._toJSONString(data));
  433.   },
  434.  
  435.   _sendData: function MS__sendData(data) {
  436.     if (gApp.appBuildID == '0000000000')
  437.       return;
  438.  
  439.     try {
  440.       var hr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
  441.         .createInstance(Ci.nsIXMLHttpRequest);
  442.  
  443.       var self = this;
  444.       hr.onerror = function(evt) { self._onError(evt); };
  445.       hr.onload  = function(evt) { self._onLoad(evt);  };
  446.  
  447.       hr.detachLoadGroup = true;
  448.       hr.open('POST', LOGGING_URL);
  449.       hr.send(data);
  450.     }
  451.     catch (e) {
  452.       this._onError(null);
  453.     }
  454.   },
  455.   _onError: function MS__onError(evt) {
  456.   },
  457.   _onLoad: function MS__onLoad(evt) {
  458.   },
  459.  
  460.   /* The next two functions taken from nsSessionStore.js */
  461.  
  462.   /**
  463.    * safe eval'ing
  464.    */
  465.   _safeEval: function sss_safeEval(aStr) {
  466.     var s = new Components.utils.Sandbox("about:blank");
  467.     return Components.utils.evalInSandbox(aStr, s);
  468.   },
  469.  
  470.   /**
  471.    * Converts a JavaScript object into a JSON string
  472.    * (see http://www.json.org/ for the full grammar).
  473.    *
  474.    * The inverse operation consists of eval("(" + JSON_string + ")");
  475.    * and should be provably safe.
  476.    *
  477.    * @param aJSObject is the object to be converted
  478.    * @return the object's JSON representation
  479.    */
  480.   _toJSONString: function sss_toJSONString(aJSObject) {
  481.     // these characters have a special escape notation
  482.     const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f",
  483.                       "\r": "\\r", '"': '\\"', "\\": "\\\\" };
  484.     // we use a single string builder for efficiency reasons
  485.     var parts = [];
  486.     
  487.     // this recursive function walks through all objects and appends their
  488.     // JSON representation to the string builder
  489.     function jsonIfy(aObj) {
  490.       if (typeof aObj == "boolean") {
  491.         parts.push(aObj ? "true" : "false");
  492.       }
  493.       else if (typeof aObj == "number" && isFinite(aObj)) {
  494.         // there is no representation for infinite numbers or for NaN!
  495.         parts.push(aObj.toString());
  496.       }
  497.       else if (typeof aObj == "string") {
  498.         aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
  499.           // use the special escape notation if one exists, otherwise
  500.           // produce a general unicode escape sequence
  501.           return charMap[$0] ||
  502.             "\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
  503.         });
  504.         parts.push('"' + aObj + '"')
  505.       }
  506.       else if (aObj == null) {
  507.         parts.push("null");
  508.       }
  509.       else if (aObj instanceof Array) {
  510.         parts.push("[");
  511.         for (var i = 0; i < aObj.length; i++) {
  512.           jsonIfy(aObj[i]);
  513.           parts.push(",");
  514.         }
  515.         if (parts[parts.length - 1] == ",")
  516.           parts.pop(); // drop the trailing colon
  517.         parts.push("]");
  518.       }
  519.       else if (typeof aObj == "object") {
  520.         parts.push("{");
  521.         for (var key in aObj) {
  522.           jsonIfy(key.toString());
  523.           parts.push(":");
  524.           jsonIfy(aObj[key]);
  525.           parts.push(",");
  526.         }
  527.         if (parts[parts.length - 1] == ",")
  528.           parts.pop(); // drop the trailing colon
  529.         parts.push("}");
  530.       }
  531.       else {
  532.         throw new Error("No JSON representation for this object!");
  533.       }
  534.     }
  535.     jsonIfy(aJSObject);
  536.     
  537.     var newJSONString = parts.join(" ");
  538.     // sanity check - so that API consumers can just eval this string
  539.     if (/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
  540.       newJSONString.replace(/"(\\.|[^"\\])*"/g, "")
  541.     ))
  542.       throw new Error("JSON conversion failed unexpectedly!");
  543.     
  544.     return newJSONString;
  545.   },
  546.  
  547.   getInterfaces: function MS_getInterfaces(countRef) {
  548.     var interfaces = [Ci.flockIMetricsService, Ci.flockIRDFObserver,
  549.                       Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsIClassInfo,
  550.                       Ci.nsISupports];
  551.     countRef.value = interfaces.length;
  552.     return interfaces;
  553.   },
  554.   getHelperForLanguage: function MS_getHelperForLanguage(language) {
  555.     return null;
  556.   },
  557.   contractID: MS_CONTRACTID,
  558.   classDescription: MS_CLASSNAME,
  559.   classID: MS_CLASSID,
  560.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  561.   flags: Ci.nsIClassInfo.SINGLETON,
  562.  
  563.   QueryInterface: function MS_QueryInterface(iid) {
  564.     if (iid.equals(Ci.flockIMetricsService) ||
  565.         iid.equals(Ci.flockIRDFObserver) ||
  566.         iid.equals(Ci.nsITimerCallback) ||
  567.         iid.equals(Ci.nsIObserver) ||
  568.         iid.equals(Ci.nsIClassInfo) ||
  569.         iid.equals(Ci.nsISupports))
  570.       return this;
  571.     throw Cr.NS_ERROR_NO_INTERFACE;
  572.   }
  573. }
  574.  
  575.  
  576. function GenericComponentFactory(ctor) {
  577.   this._ctor = ctor;
  578. }
  579.  
  580. GenericComponentFactory.prototype = {
  581.  
  582.   _ctor: null,
  583.  
  584.   // nsIFactory
  585.   createInstance: function(outer, iid) {
  586.     if (outer != null)
  587.       throw Cr.NS_ERROR_NO_AGGREGATION;
  588.     return (new this._ctor()).QueryInterface(iid);
  589.   },
  590.  
  591.   // nsISupports
  592.   QueryInterface: function(iid) {
  593.     if (iid.equals(Ci.nsIFactory) ||
  594.         iid.equals(Ci.nsISupports))
  595.       return this;
  596.     throw Cr.NS_ERROR_NO_INTERFACE;
  597.   },
  598. };
  599.  
  600. var Module = {
  601.   QueryInterface: function(iid) {
  602.     if (iid.equals(Ci.nsIModule) ||
  603.         iid.equals(Ci.nsISupports))
  604.       return this;
  605.  
  606.     throw Cr.NS_ERROR_NO_INTERFACE;
  607.   },
  608.  
  609.   getClassObject: function(cm, cid, iid) {
  610.     if (!iid.equals(Ci.nsIFactory))
  611.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  612.  
  613.     if (cid.equals(MS_CLASSID))
  614.       return new GenericComponentFactory(MetricsService)
  615.  
  616.     throw Cr.NS_ERROR_NO_INTERFACE;
  617.   },
  618.  
  619.   registerSelf: function(cm, file, location, type) {
  620.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  621.     cr.registerFactoryLocation(MS_CLASSID, MS_CLASSNAME, MS_CONTRACTID,
  622.                                file, location, type);
  623.  
  624.     var catman = Cc['@mozilla.org/categorymanager;1']
  625.       .getService(Ci.nsICategoryManager);
  626.     catman.addCategoryEntry('flock-startup', MS_CLASSNAME,
  627.                             'service,' + MS_CONTRACTID,
  628.                             true, true);
  629.   },
  630.  
  631.   unregisterSelf: function(cm, location, type) {
  632.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  633.     cr.unregisterFactoryLocation(MS_CLASSID, location);
  634.   },
  635.  
  636.   canUnload: function(cm) {
  637.     return true;
  638.   },
  639. };
  640.  
  641. function NSGetModule(compMgr, fileSpec)
  642. {
  643.   return Module;
  644. }
  645.